package com.foreveross.crawl.adapter.sub.impl20140402.v3;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sf.json.JSONObject;

import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import com.foreveross.crawl.adapter.AbstractAdapter;
import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.common.exception.self.UnableParseRouteTypeException;
import com.foreveross.crawl.domain.airfreight.AbstractPlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.CabinRelationEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.domain.airfreight.single.SinglePlaneInfoEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.taskservice.common.bean.TaskModel;

/**
 * 香港航空
 * 2014年7月不考虑外国起飞的情况，外国起飞币种不是人民币，改动把币种字段改一下即可
 * @author stb
 *
 */
public class HongKongAirAdapter extends AbstractAdapter {

	private static Map<String,String> cabinType = new HashMap<String, String>(){
		{
			put("ECONOMY", "经济舱");
			put("BUSINESS", "商务舱");
		}
	};
	
	//去程请求次数
	//private static final int QUERYCOUNT = 3;
	//往返请求次数，尝试三次失败则任务失败
	private static final int QUERYROUNDCOUNT = 3;
	//请求税价页面，试三次不成功则放弃
	private static final int QUERYTAXCOUNT = 3;

	private static String onewWay = "OW"; //单程
	private static String roundTrip = "RT";//往返
	
	private static String url = "https://book.hongkongairlines.com/hxet/reservation/AVQuery.do"; //请求到舱位页面
	private static String brandUrl = "https://book.hongkongairlines.com/hxet/reservation/reservation/BrandQuery.do";//具体查询航班
	private static String passUrl = "https://book.hongkongairlines.com/hxet/reservation/forPassengerInput.do";//乘客页
	private static String roundUrl = "https://book.hongkongairlines.com/hxet/reservation/reservation/FltReturn.do";//往返每点一次去程都回请求一次
	private static SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
	
	private static String regex = "<tr>(<th.*?</th>)*<td.*?value='(.*?)'.*?</td></tr>((<tr>(<td.*?</td>)</tr>)*)";
	private static Pattern pattern = Pattern.compile(regex);
	private static String tdRegex = "<tr><td>(.*?)</td><td>(.*?)</td><td>(.*?)</td><td>(.*?)</td><td>(.*?)</td><td>(.*?)</td>(<td.*?>(.*?)</td>)?</tr>";
	private static Pattern tdPattern = Pattern.compile(tdRegex);
	private static String roRegex = "<tr>(<th.*?</th>)*<td.*?fltno='(.*?)'.*?</td></tr>((<tr>(<td.*?</td>)</tr>)*)";
	private static Pattern roPattern = Pattern.compile(roRegex);
	
	private static String carrierKey = "HX";
	private static String carrierName = "港航";
	private static String carrierFullName = "香港航空公司";
	
	//没有航班的舱位
	private int noFlightCabinCount = 0;
	
	public HongKongAirAdapter(TaskModel taskQueue) {
		super(taskQueue);
	}

	@Override
	public List<Object> paraseToVo(Object fetchObject) throws Exception {
		switch (super.getRouteType()) {
		case INTERNATIONAL_ONEWAY:
			return paraseInterOneWay(fetchObject);
		case INTERNATIONAL_ROUND:
			return paraseInterRound(fetchObject);
		}
		return null;
	}

	@Override
	public String getUrl() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object fetch(String url) throws Exception {
		//只查国际航班，国内是跳转到海南航空的
		switch (super.getRouteType()) {
		case  INTERNATIONAL_ROUND:
			return fetchInterRound();
		case INTERNATIONAL_ONEWAY:
			return fetchInterOneWay();
		default : throw new UnableParseRouteTypeException(taskQueue, super.getRouteType().getName());
		}
	}
	
	/**
	 * 抓取国际单程
	 * @return
	 * @throws Exception 
	 */
	private Object fetchInterOneWay() throws Exception{
		logger.info("抓取单程开始----------");
		List<AbstractPlaneInfoEntity> result = new ArrayList<AbstractPlaneInfoEntity>();
		HttpPost p = null;
		//按航班号保存每个航班信息
		Map<String,AbstractPlaneInfoEntity> planeInfoMap = new HashMap<String, AbstractPlaneInfoEntity>();
		//按航班号+舱位号保存每个舱位信息
		Map<String,CabinEntity> cabinMap = new HashMap<String, CabinEntity>();
		Document doc = null;
		try{
			//根据舱位类型获得具体舱位信息
			for(String key : cabinType.keySet()){
				logger.info("抓取舱位" + cabinType.get(key)  + "---------");
				doc = AVQuery(p,url,key,onewWay);
				Elements radios = doc.select(".brandinfoclass li input[type=radio]");
				//如果没有舱位选择，有可能  1已经卖完座位或者没符合条件的航班
				//2香港到外国城市需要中转的，或外国城市到香港需要中转的，这些情况有时只有经济舱和商务舱
				if(radios.isEmpty()){
					boolean noFlight = false;
					try{
						noFlight = doc.select(".dattbl2").get(0).select("tr").get(1).text().indexOf("没有航班") >= 0;
					}catch(Exception e){
						noFlight = doc.select(".dattbl2-1").get(0).select("tr").get(1).text().indexOf("售罄") >= 0;
					}
					if(noFlight){
						noFlightCabinCount ++;
						logger.info("舱位：" + cabinType.get(key) + "已售完");
						continue;
					}
					
					Element tbOw = doc.getElementById("tableOW");
					List<FlightNo_List> lis = getFlight(tbOw);
					for(int i = 0;i < lis.size();i++){
						FlightNo_List flightList = lis.get(i);
						Document passDoc = null;
						for(int c = 0;c < QUERYTAXCOUNT;c++){
							try{
								passDoc = postQuery(p,doc,i,-1);
								break;
							}catch(Exception e){
								logger.info("尝试抓取合计价第" + (c + 1) + "次失败");
								switchProxyipByHttClient();
							}
						}
						
						//这种航班第二层页面是没有价格信息的，如果没抓到就报错
						if(passDoc == null){
							throw new Exception("单程获取价格失败");
						}
						parseInerOneWay(planeInfoMap, cabinMap, passDoc, flightList, key, cabinType.get(key),null);
					}
				}else{
					Element random = doc.select("#random").get(0);
					//根据每种舱位获得航班信息
					for(Element e : radios){
						JSONObject jsonObject = queryByCabin(e,random);
						String proName = e.parent().text().replaceAll("<input.*/>", "");
						String fltcontext = jsonObject.getString("fltcontext");
						Matcher m = pattern.matcher(fltcontext);
						while (m.find()) {
							String eachFilght = m.group(3);
							String fltkey = m.group(2);
							Matcher fM = tdPattern.matcher(eachFilght);
							FlightNo_List flightList = getFlight(fM);
							//查询舱位对应航班的报价，每一次查询都是动态的
							String pricePage = fetchPrice(doc,e,fltkey);
							JSONObject priceObject = JSONObject.fromObject(pricePage);
							//paycur是根据选择的城市出发地点确定货币的 convertresult是转化后的人民币，如果要针对非中国的城市起点，用这个字段
							String price = priceObject.getString("paycur");
							if(price.indexOf("CNY") == -1){
								logger.info("抓取到的金额为非人民币，忽略");
								continue;
							}
							//查询金额，税费等
							Document passDoc = null;
							for(int c = 0; c < QUERYTAXCOUNT;c++){
								try{
									passDoc = postQuery(p,fltkey,e,random,doc);
									break;
								}catch(Exception exce){
									logger.info("尝试抓取合计价第" + (c + 1) + "次失败");
									switchProxyipByHttClient();
								}
							}
							parseInerOneWay(planeInfoMap, cabinMap, passDoc, flightList, key, proName,price);
						}
					}
				}
			}
			
			if(noFlightCabinCount == cabinType.size()){
				throw new FlightInfoNotFoundException("所有舱位都无航班或者售罄");
			}
			result.addAll(planeInfoMap.values());
			//设置最高和最低价格
			PlaneInfoEntityBuilder.buildLimitPrice(result);
		}finally{
			if(p != null){p.releaseConnection();}
 		}
		return result;
	}
	
	/**
	 * 抓取国际往返
	 * @return
	 * @throws Exception
	 */
	private Object fetchInterRound() throws Exception{
		logger.info("抓取往返开始------------");
		List<AbstractPlaneInfoEntity> result = new ArrayList<AbstractPlaneInfoEntity>();
		//按航班号保存每个航班信息
		Map<String,AbstractPlaneInfoEntity> planeInfoMap = new HashMap<String, AbstractPlaneInfoEntity>();
		//按全程航班号保存每个航班信息
		Map<String,AbstractPlaneInfoEntity> returnMap = new HashMap<String, AbstractPlaneInfoEntity>();
		//按航班号+舱位号保存每个舱位信息
		Map<String,CabinEntity> cabinMap = new HashMap<String, CabinEntity>();
		//按全程航班号+舱位号保存每个舱位信息
		Map<String,ReturnCabinEntity> returnCabinMap = new HashMap<String, ReturnCabinEntity>();
		HttpPost p = null;
		Document doc = null;
		try{
			for(String key : cabinType.keySet()){
				logger.info("抓取舱位" + cabinType.get(key) + "----------");
				doc = AVQuery(p, url, key,roundTrip);
				Elements radios = doc.select(".brandinfoclass li input[type=radio]");
				//如果没有舱位选择，有可能  1已经卖完座位或者没符合条件的航班
				//2香港到外国城市需要中转的，或外国城市到香港需要中转的，这些情况有时只有经济舱和商务舱
				if(radios.isEmpty()){
					boolean noFlight = false;
					try{
						noFlight = doc.select(".dattbl2").get(0).select("tr").get(1).text().indexOf("没有航班") >= 0;
					}catch(Exception e){
						noFlight = doc.select(".dattbl2-1").get(0).select("tr").get(1).text().indexOf("售罄") >= 0;
					}
					if(!noFlight){
						try{
							noFlight = doc.select(".dattbl2").get(1).select("tr").get(1).text().indexOf("没有航班") >= 0;
						}catch(Exception e){
							noFlight = doc.select(".dattbl2-1").get(1).select("tr").get(1).text().indexOf("售罄") >= 0;
						}
					}
					if(noFlight){
						noFlightCabinCount ++;
						logger.info("舱位：" + cabinType.get(key) + "已售完");
						continue;
					}
					
					Element tbOw = doc.getElementById("tableOW");
					List<FlightNo_List> lis = getFlight(tbOw);
					for(int i = 0;i < lis.size();i++){
						FlightNo_List flightList = lis.get(i);
						Element tbRt = doc.getElementById("tableRT");
						List<FlightNo_List> returnLis = getFlight(tbRt);
						for(int j = 0;j < returnLis.size();j++){
							Document passDoc = null;
							for(int k = 0;k < QUERYTAXCOUNT;k++){
								try{
										passDoc = postQuery(p,doc,i,j);
										break;
								}catch(Exception e){
									logger.error("第"  + (k +1) + "次查询税费失败" , e);
									switchProxyipByHttClient();
								}
							}
							//这种情况必须请求到价钱，第二层页面无价钱
							if(passDoc == null){
								throw new Exception("请求价格失败");
							}
							parseRoundTrip(planeInfoMap,returnMap,cabinMap,returnCabinMap,passDoc, flightList,returnLis.get(j),key, cabinType.get(key),null);
						}
					}
				}else{
					Element random = doc.select("#random").get(0);
					//根据每种舱位获得航班信息
					for(Element e : radios){
						JSONObject jsonObject = queryByCabin(e,random);
						String proName = e.parent().text().replaceAll("<input.*/>", "");
						String fltcontext = jsonObject.getString("fltcontext");
						//0去程 1回程
						String[] round = fltcontext.split("回程");
						Matcher m = roPattern.matcher(round[0]);
						while (m.find()) {
							String eachFilght = m.group(3);
							String fltno = m.group(2);
							Matcher fM = null;
							FlightNo_List  flightList = null;
							Matcher rm = null;
							for(int i=0;i < QUERYROUNDCOUNT;i++){
								try{
									//每次点去程都请求一次
									fltcontext = queryByCabinAndTo(p,e, fltno).replaceAll("\"", "'");
									 fM = tdPattern.matcher(eachFilght);
									//去程信息
									flightList = getFlight(fM);
									 rm = pattern.matcher(fltcontext);
									 break;
								}catch(Exception ex){
									logger.error("第" + (i + 1) + "次请求返程失败", ex);
									if(i == QUERYROUNDCOUNT - 1){
										throw ex;
									}
									switchProxyipByHttClient();
								}
							}
							while(rm.find()){
								String returnFlight = rm.group(3);
								String returnFltkey = rm.group(2);
								Matcher rfM = tdPattern.matcher(returnFlight);
								FlightNo_List returnFlightList = getFlight(rfM);
								
								//TODO 查询舱位对应航班的报价，每一次查询都是动态的 returnFltkey这个值待确认
								String pricePage = fetchPrice(doc,e,returnFltkey);
								JSONObject priceObject = JSONObject.fromObject(pricePage);
								//paycur是根据选择的城市出发地点确定货币的 convertresult是转化后的人民币，如果要针对非中国的城市起点，用这个字段
								String price = priceObject.getString("paycur");
								if(price.indexOf("CNY") == -1){
									logger.info("抓取到的金额为非人民币，忽略");
									continue;
								}
								//查询金额，税费等
								Document passDoc = null;
								for(int k = 0;k < QUERYTAXCOUNT;k++){
									try{
										 passDoc = postQuery(p,returnFltkey,e,random,doc);
										 break;
									}catch(Exception ex){
										logger.error("第" + (k + 1) + "次请求税费失败",ex);
										switchProxyipByHttClient();
									}
								}
								parseRoundTrip(planeInfoMap,returnMap,cabinMap,returnCabinMap,passDoc, flightList,returnFlightList,key, proName,price);
							}
						}
					}
				}
			}
			
			if(noFlightCabinCount == cabinType.size()){
				throw new FlightInfoNotFoundException("所有舱位都没有航班或者已售罄");
			}
			result.addAll(planeInfoMap.values());
			PlaneInfoEntityBuilder.buildLimitPrice(result);
		}finally{
			if(p != null){p.releaseConnection();}
		}
		return result;
	}
	
	//航班中转机场码信息
	private class AirPort{
		private String flightNo;
		private String fromAirPort;
		private String toAirPort;
		public String getFlightNo() {
			return flightNo;
		}
		public void setFlightNo(String flightNo) {
			this.flightNo = flightNo;
		}
		public String getFromAirPort() {
			return fromAirPort;
		}
		public void setFromAirPort(String fromAirPort) {
			this.fromAirPort = fromAirPort;
		}
		public String getToAirPort() {
			return toAirPort;
		}
		public void setToAirPort(String toAirPort) {
			this.toAirPort = toAirPort;
		}
	}
	
	/**
	 * 获取航班和机场码
	 * @param doc
	 * @return
	 */
	private List<AirPort> getAirPort(Document doc){
		List<AirPort> list = new ArrayList<AirPort>();
		Elements trs = doc.getElementById("fareBasisInfo").select(".dattbl9").get(0).select("tr:gt(0)");
		for(Element tr : trs){
			AirPort air = new AirPort();
			Elements tds = tr.select("td");
			String[] arr = tds.get(0).text().split("-");
			air.setFlightNo(tds.get(1).text().trim());
			air.setFromAirPort(arr[0].replaceAll("[^A-Z]", ""));
			air.setToAirPort(arr[1].replaceAll("[^A-Z]", ""));
			list.add(air);
		}
		return list;
	}
	
	//每一条子航班信息
	private class FlightInfo{
		//航班号
		private String flightNo;
		//航班开始时间
		private String startTime;
		//航班出发城市
		private String startCity;
		//航班结束时间
		private String endTime;
		//航班到达城市
		private String endCity;
		//航班类型
		private String flightType;
		//座位信息
		private String seatInfo;
		//停留时长
		private String stopTime;
		
		public String getFlightNo() {
			return flightNo;
		}
		public void setFlightNo(String flightNo) {
			this.flightNo = flightNo;
		}
		public String getStartTime() {
			return startTime;
		}
		public void setStartTime(String startTime) {
			this.startTime = startTime;
		}
		public String getEndTime() {
			return endTime;
		}
		public void setEndTime(String endTime) {
			this.endTime = endTime;
		}
		
		public String getFlightType() {
			return flightType;
		}
		public void setFlightType(String flightType) {
			this.flightType = flightType;
		}
		public String getSeatInfo() {
			return seatInfo;
		}
		public void setSeatInfo(String seatInfo) {
			this.seatInfo = seatInfo;
		}
		public String getStartCity() {
			return startCity;
		}
		public void setStartCity(String startCity) {
			this.startCity = startCity;
		}
		public String getEndCity() {
			return endCity;
		}
		public void setEndCity(String endCity) {
			this.endCity = endCity;
		}
		public String getStopTime() {
			return stopTime;
		}
		public void setStopTime(String stopTime) {
			this.stopTime = stopTime;
		}
		
	}
	
	/**
	 * 存一条航线的所有航班号和航线信息
	 * @author stb
	 *
	 */
	private class FlightNo_List{
		//一条航线的所有航班号
		private StringBuilder grFilghtNo;
		//航线信息
		private List<FlightInfo> flightInfoList;
		
		public StringBuilder getGrFilghtNo() {
			return grFilghtNo;
		}
		public void setGrFilghtNo(StringBuilder grFilghtNo) {
			this.grFilghtNo = grFilghtNo;
		}
		public List<FlightInfo> getFlightInfoList() {
			return flightInfoList;
		}
		public void setFlightInfoList(List<FlightInfo> flightInfoList) {
			this.flightInfoList = flightInfoList;
		}
	}
	
	/**
	 * 组装一个航线
	 * @param fM
	 * @return
	 */
	private FlightNo_List getFlight(Matcher fM){
		//1航班号 2出发时间 3抵达时间 4时段 5停止 6类型 8现有机位
		FlightNo_List flightList = new FlightNo_List();
		List<FlightInfo> list = new ArrayList<FlightInfo>();
		StringBuilder sb = new StringBuilder();
		while (fM.find()) {
			FlightInfo flightInfo = new FlightInfo();
			flightInfo.setFlightNo(fM.group(1).replaceAll("\\s", ""));
			sb.append(fM.group(1).replaceAll("\\s", ""));
			String[] start_city = fM.group(2).split("<br.*?>");
			if(start_city.length == 1){
				flightInfo.setStartTime(start_city[0]);
			}else{
				flightInfo.setStartCity(start_city[0]);
				flightInfo.setStartTime(taskQueue.getFlightDate().substring(0,4) + "-" +start_city[1].replaceAll("月", "-").replaceAll("日", ""));
			}
			String[] end_city = fM.group(3).split("<br.*?>");
			if(end_city.length == 1){
				flightInfo.setEndTime(end_city[0]);
			}else{
				flightInfo.setEndCity(end_city[0]);
				flightInfo.setEndTime(taskQueue.getFlightDate().substring(0,4) + "-" + end_city[1].replaceAll("月", "-").replaceAll("日", ""));
			}
			flightInfo.setFlightType(fM.group(6));
			if(fM.group(8) != null){
				flightInfo.setSeatInfo(fM.group(8).replaceAll("<.*?span.*?>",""));
			}
			flightInfo.setStopTime(fM.group(5));
			list.add(flightInfo);
		}
		flightList.setGrFilghtNo(sb);
		flightList.setFlightInfoList(list);
		return flightList;
	}
	
	/**
	 * 组装一个航线
	 * @param tb
	 * @return
	 */
	private List<FlightNo_List> getFlight(Element tb){
		List<FlightNo_List> lis = new ArrayList<FlightNo_List>();
		FlightNo_List flightList = null;
		List<FlightInfo> list = null;
		StringBuilder sb = null;
		int count = 0;
		for(Element tr : tb.select("tr")){
			//第一条记录是行标题信息
			if(tr.hasAttr("id")){
				flightList = new FlightNo_List();
				list = new ArrayList<FlightInfo>();
				sb = new StringBuilder();
				flightList.setGrFilghtNo(sb);
				flightList.setFlightInfoList(list);
				lis.add(flightList);
				count = 0;
			}else{
				count ++;
				Elements tds = tr.select("td");
				FlightInfo flightInfo = new FlightInfo();
				String flightNo = tds.get(0).html().trim().replaceAll("<br.*?>", "");
				sb.append(flightNo);
				flightInfo.setFlightNo(flightNo);
				String[] start_city = tds.get(1).html().split("<br.*?>\\s*");
				flightInfo.setStartCity(start_city[0]);
				flightInfo.setStartTime(getStandDate(taskQueue.getFlightDate().substring(0,4) + "-" 
						+ start_city[1].trim().replaceAll("月", "-").replaceAll("日", "")));
				String[] end_city = tds.get(2).html().split("<br.*?>\\s");
				flightInfo.setEndCity(end_city[0]);
				flightInfo.setEndTime(getStandDate(taskQueue.getFlightDate().substring(0,4) + "-" 
						+ end_city[1].trim().replaceAll("月", "-").replaceAll("日", "")));
				if(count == 1){
					flightInfo.setFlightType(tds.get(5).text());
					flightInfo.setSeatInfo(tds.get(6).text().replaceAll("<.*?span.*?>",""));
				}else{
					flightInfo.setFlightType(tds.get(4).text());
					flightInfo.setSeatInfo(tds.get(5).text().replaceAll("<.*?span.*?>",""));
				}
				list.add(flightInfo);
			}
		}
		return lis;
	}
	
	
	/**
	 * 组装单程实体
	 * @param planeInfoMap
	 * @param cabinMap
	 * @param passDoc
	 * @param flightList
	 * @param key
	 * @param proName
	 * @throws Exception
	 */
	private void parseInerOneWay(Map<String,AbstractPlaneInfoEntity> planeInfoMap,Map<String,CabinEntity> cabinMap,
			Document passDoc,FlightNo_List flightList,String key,String proName,String price) throws Exception{
		if(passDoc == null && price == null){
			throw new IllegalArgumentException("价格请求不到");
		}
		String grFlightNo = flightList.getGrFilghtNo().toString();
		List<FlightInfo> list = flightList.getFlightInfoList();
		if(list.isEmpty()){
			throw new Exception("抓取到的数据有问题");
		}
		
		String originalPrice = null;
		String taxPrice = null;
		if(passDoc != null){
			//价格
			try{
				price = passDoc.select("table.dattbl9").get(0).select("tr").get(1).select("td").get(4).text();
				price = price.replaceAll("[^\\d\\.]", "");
				//总价 包括税费等
				originalPrice = passDoc.getElementById("totalPriceForPopUp").val();
				taxPrice = String.valueOf(Double.valueOf(originalPrice) - Double.valueOf(price));
			}catch(Exception e){
				//logger.info(passDoc);
				logger.info(e);
				if(price == null){
					if(passDoc.getElementById("error_page").html().indexOf("该行程无可用的运价信息 ") >= 0){
						noFlightCabinCount ++;
						return;
					}else{
						throw e;
					}
				}
			}
			/*if(price.indexOf("CNY") == -1){
				logger.info("抓取到的金额为非人民币，忽略");
				return;
			}*/
		}
		price = price.replaceAll("[^\\d\\.]", "");
		SinglePlaneInfoEntity entity = null;
		FlightInfo firstInfo = list.get(0);
		FlightInfo lastInfo = list.get(list.size() - 1);
		if(!planeInfoMap.containsKey(grFlightNo)){
			String carrKey =  carrierKey;
			entity = (SinglePlaneInfoEntity)PlaneInfoEntityBuilder
						.buildPlaneInfo(taskQueue,carrKey, getCarrierName(carrKey), getCarrierFullName(carrKey), firstInfo.getStartTime(),
								lastInfo.getEndTime(), firstInfo.getFlightNo(), null, null, null, firstInfo.getFlightType());
			entity.setCurrency("CNY");
			//有中转信息
			if(list.size() > 1){
				//航班机场码信息
				List<AirPort> portList = null;
				if(passDoc != null){
					 portList = getAirPort(passDoc);
				}
				if(portList != null  && portList.size() != list.size()){
					throw new Exception("两次请求取得航班段不等错误");
				}
				for(int i =0; i < list.size();i++){
					FlightInfo info = list.get(i);
					AirPort airPort = portList == null ? null :  portList.get(i);
					if(airPort != null && !info.getFlightNo().equals(airPort.getFlightNo())){
						throw new Exception("两次请求的航班号不等");
					}
				    carrKey =  info.getFlightNo().substring(0,2);
					TransitEntity transiEntity = PlaneInfoEntityBuilder.buildTransitEntity(firstInfo.getFlightNo(), info.getFlightNo(), carrKey,
							getCarrierName(carrKey), getCarrierFullName(carrKey), airPort == null ? null : airPort.getFromAirPort(), info.getStartCity(), 
							airPort == null? null : airPort.getToAirPort(), info.getEndCity(), info.getFlightType(), TransitEntity.class);
					transiEntity.setStartTime(format.parse(info.getStartTime()));
					transiEntity.setEndTime(format.parse(info.getEndTime()));
					//transiEntity.setStayTime(stayTime);
					entity.getTransits().add(transiEntity);
				}
			}
			planeInfoMap.put(grFlightNo, entity);
		}else{
		    entity = (SinglePlaneInfoEntity)planeInfoMap.get(grFlightNo);
		}
		
		//增加舱位信息
		if(!cabinMap.containsKey(grFlightNo + proName)){
			CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinType.get(key), null, proName,
					taxPrice,price, originalPrice , null, firstInfo.getSeatInfo(),
					CabinEntity.class);
			entity.getCabins().add(cabin);
			cabinMap.put(grFlightNo + proName,cabin);
		}
	}
	
	/**
	 * 组装往返实体
	 * @param planeInfoMap 按去程航班号保存的航班信息
	 * @param returnMap 按全程航班号保存的回程信息
	 * @param cabinMap 按舱位名称保存的舱位信息
	 * @param passDoc 价钱和航线机场码信息
	 * @param flightList 去程航线信息
	 * @param returnFlightList 返程航线信息
	 * @param key 舱位key
	 * @param proName 舱位名称
	 * @param price 价格
	 * @throws Exception
	 */
	private void parseRoundTrip(Map<String,AbstractPlaneInfoEntity> planeInfoMap,Map<String,AbstractPlaneInfoEntity> returnMap,
			Map<String,CabinEntity> cabinMap,Map<String,ReturnCabinEntity> returnCabinMap,Document passDoc,FlightNo_List flightList,
			FlightNo_List returnFlightList,String key,String proName,String price) throws Exception{
		if(passDoc == null && price == null){
			throw new IllegalArgumentException("价格请求不到");
		}
		String grFlightNo = flightList.getGrFilghtNo().toString();
		List<FlightInfo> list = flightList.getFlightInfoList();
		
		String flightNos = grFlightNo + "#" + returnFlightList.getGrFilghtNo();
		List<FlightInfo> returnList = returnFlightList.getFlightInfoList();
		if(list.isEmpty()){
			throw new Exception("抓取到的数据有问题");
		}
		//价格
		String originalPrice = null;
		String taxPrice = null;
		if(passDoc != null){
			try {
				price = passDoc.select("table.dattbl9").get(0).select("tr").get(1).select("td").get(4).text();
				price = price.replaceAll("[^\\d\\.]", "");
				//总价 包括税费等
				originalPrice = passDoc.getElementById("totalPriceForPopUp").val();
				taxPrice = String.valueOf(Double.valueOf(originalPrice) - Double.valueOf(price));
			} catch (Exception e) {
				//logger.info(passDoc);
				if(price == null){
					if(passDoc.getElementById("error_page").html().indexOf("该行程无可用的运价信息 ") >= 0){
						noFlightCabinCount ++;
						return;
					}else{
						throw e;
					}
				}
			}
		}
		/*if(price.indexOf("CNY") == -1){
			logger.info("抓取到的金额为非人民币，忽略");
			return;
		}*/
		price = price.replaceAll("[^\\d\\.]", "");
		
		List<AirPort> portList = passDoc == null ? null : getAirPort(passDoc);
		
		DoublePlaneInfoEntity entity = null;
		List<AirPort> goList = null;
		List<AirPort> reList = null;
		FlightInfo firstInfo = list.get(0);
		FlightInfo lastInfo = list.get(list.size() - 1);
		if(!planeInfoMap.containsKey(grFlightNo)){
			String carrKey =  carrierKey;
			entity = (DoublePlaneInfoEntity)PlaneInfoEntityBuilder
						.buildPlaneInfo(taskQueue,carrKey, getCarrierName(carrKey), getCarrierFullName(carrKey), firstInfo.getStartTime(),
								lastInfo.getEndTime(), firstInfo.getFlightNo(), null, null, null, firstInfo.getFlightType());
			entity.setCurrency("CNY");
			//有中转信息
			if(list.size() > 1){
				//航班机场码信息
				goList = portList == null ? null : portList.subList(0, portList.size() / 2);
				if(goList != null && goList.size() != list.size()){
					throw new Exception("两次请求取得航班段不等错误");
				}
				for(int i =0; i < list.size();i++){
					FlightInfo info = list.get(i);
					AirPort airPort = goList == null ? null : goList.get(i);
					if(airPort != null && !info.getFlightNo().equals(airPort.getFlightNo())){
						throw new Exception("两次请求的航班号不等");
					}
				    carrKey =  info.getFlightNo().substring(0,2);
					TransitEntity transiEntity = PlaneInfoEntityBuilder.buildTransitEntity(firstInfo.getFlightNo(), info.getFlightNo(), carrKey,
							getCarrierName(carrKey), getCarrierFullName(carrKey),airPort == null ? null : airPort.getFromAirPort(), info.getStartCity(), 
									airPort == null ? null : airPort.getToAirPort(), info.getEndCity(), info.getFlightType(), TransitEntity.class);
					transiEntity.setStartTime(format.parse(info.getStartTime()));
					transiEntity.setEndTime(format.parse(info.getEndTime()));
					//transiEntity.setStayTime(stayTime);
					entity.getTransits().add(transiEntity);
				}
			}
			planeInfoMap.put(grFlightNo, entity);
		}else{
		    entity = (DoublePlaneInfoEntity)planeInfoMap.get(grFlightNo);
		}
		
		//增加回程信息
		ReturnDoublePlaneInfoEntity returnEntity = null;
		FlightInfo returnFirstInfo = returnList.get(0);
		FlightInfo returnLastInfo = returnList.get(returnList.size() - 1);
		if(!returnMap.containsKey(flightNos)){
			returnEntity = (ReturnDoublePlaneInfoEntity)PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue,
					carrierKey, carrierName, carrierFullName,returnFirstInfo.getStartTime(), returnLastInfo.getEndTime(), 
					returnFirstInfo.getFlightNo(), null, null, null, returnFirstInfo.getFlightType(),ReturnDoublePlaneInfoEntity.class);
			if(returnList.size() > 1){
				reList = portList == null ? null : portList.subList(portList.size() / 2,portList.size());
				//航班机场码信息
				if(reList != null && returnList.size() != reList.size()){
					throw new Exception("两次请求取得航班段不等错误");
				}
				for(int i =0; i < returnList.size();i++){
					FlightInfo info = returnList.get(i);
					AirPort airPort = reList == null ? null : reList.get(i);
					if(airPort != null && !info.getFlightNo().equals(airPort.getFlightNo())){
						throw new Exception("两次请求的航班号不等");
					}
				    String carrKey =  info.getFlightNo().substring(0,2);
					ReturnTransitEntity returnTransEntity = PlaneInfoEntityBuilder.buildTransitEntity(returnFirstInfo.getFlightNo(), info.getFlightNo(), carrKey,
							getCarrierName(carrKey), getCarrierFullName(carrKey), airPort == null ? null : airPort.getFromAirPort(), info.getStartCity(), 
									airPort == null ? null : airPort.getToAirPort(), info.getEndCity(), info.getFlightType(), ReturnTransitEntity.class);
					returnTransEntity.setStartTime(format.parse(info.getStartTime()));
					returnTransEntity.setEndTime(format.parse(info.getEndTime()));
					//transiEntity.setStayTime(stayTime);
					returnEntity.getReturnTransits().add(returnTransEntity);
				}
			}
			entity.getReturnPlaneInfos().add(returnEntity);
			returnMap.put(flightNos,returnEntity);
		}else{
			returnEntity = (ReturnDoublePlaneInfoEntity)returnMap.get(flightNos);
		}
		
		//增加舱位信息
		CabinEntity cabin = null;
		if(!cabinMap.containsKey(grFlightNo + proName)){
			cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinType.get(key), null, proName,
					taxPrice,price, originalPrice , null, firstInfo.getSeatInfo(), CabinEntity.class);
			entity.getCabins().add(cabin);
			cabinMap.put(grFlightNo + proName,cabin);
		}else{
			cabin = cabinMap.get(grFlightNo + proName);
		}
		//增加回程舱位信息
		if(!returnCabinMap.containsKey(flightNos + proName)){
			ReturnCabinEntity returnCabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinType.get(key), null, proName,
					taxPrice,price, originalPrice , null, firstInfo.getSeatInfo(), ReturnCabinEntity.class);
			returnEntity.getReturnCabins().add(returnCabin);
			
			//增加去回程关系
			Double priceDouble = Double.valueOf(price);
			Double originalPriceDouble = originalPrice == null ? null : Double.valueOf(originalPrice);
			CabinRelationEntity relation = new CabinRelationEntity();
			relation.setPlaneInfoEntity(entity);
			relation.setFullPrice(priceDouble);
			relation.setCabinId(cabin.getId());
			relation.setTaxesPrice(originalPrice == null ?  null : (originalPriceDouble - priceDouble));
			relation.setTotalFullPrice(originalPriceDouble);
			relation.setReturnCabinId(returnCabin.getId());
			entity.getCabinRelations().add(relation);
			
			returnCabinMap.put(flightNos + proName,returnCabin);
		}
	}

	//返回运营商简称
	private static String getCarrierName(String carrierKey){
		if(HongKongAirAdapter.carrierKey.equals(carrierKey)){
			return carrierName;
		}
		return null;
	}
	
	//返回运营商全程
	private static String getCarrierFullName(String carrierKey){
		if(HongKongAirAdapter.carrierKey.equals(carrierKey)){
			return carrierFullName;
		}
		return null;
	}
	
	/**
	 * 返回 yyyy-MM-dd HH:mm格式的时间
	 * @param date
	 */
	private static String getStandDate(String date){
		if(date == null){
			return null;
		}
		StringBuilder sb = new StringBuilder();
		String[] arr = date.split("-");
		sb.append(arr[0]);
		sb.append("-");
		sb.append(arr[1].length() == 1 ? ("0" + arr[1]) : arr[1]);
		sb.append("-");
		sb.append(arr[2].length() == 1 ? ("0" + arr[2]) : arr[2]);
		return sb.toString();
	}
	
	
	/**
	 * 请求航班页面查询
	 * @param p
	 * @param url
	 * @param key
	 * @return
	 * @throws Exception
	 */
	private Document AVQuery(HttpPost p,String url,String key,String tripType) throws Exception{
		p=super.getBasePost(url, getAVQueryParams(key, tripType));
		setDefaultHeadValue(p);
		HttpClient h = getHttpClient();
		Document doc = Jsoup.parse(excuteRequest(h, p, true));
		return doc;
	}
	
	/**
	 * 按舱位航班列表
	 * @param e
	 * @param random
	 * @return
	 * @throws Exception 
	 */
	private JSONObject queryByCabin(Element e,Element random) throws Exception{
		StringBuilder sb = new StringBuilder();
		sb.append(brandUrl);
		sb.append("?farefamilyname=" + e.val());
		sb.append("&brandseq=" + e.attr("seqno"));
		sb.append("&random=" + random.val());
		HttpGet g = new HttpGet(sb.toString());
		setDefaultHeadValue(g);
		HttpClient h = getHttpClient();
		JSONObject jsonObject = JSONObject.fromObject(excuteRequest(h, g, true));
		return jsonObject;
	}
	
	/**
	 * 根据舱位和去程查询信息
	 * @param e
	 * @param fltno
	 * @return
	 * @throws Exception
	 */
	private String queryByCabinAndTo(HttpPost p,Element e,String fltno) throws Exception{
		Map<String,String> map = new HashMap<String, String>();
		map.put("farefamilyname", e.val());
		map.put("fltno", fltno);
		p=super.getBasePost(roundUrl, map);
		HttpClient h = getHttpClient();
		return excuteRequest(h, p, true);
	}

	/**
	 * 下单填写信息页查询(第一种情况)
	 * @param p
	 * @param url
	 * @return
	 * @throws Exception
	 */
	private Document postQuery(HttpPost p,String fltkey,Element e,Element random,Document doc) throws Exception{
		Map<String,String> map = new HashMap<String, String>();
		map.put("fltkey", fltkey);
		map.put("brandName", e.val());
		map.put("from", doc.getElementById("nextform").select("[name=from]").get(0).val());
		map.put("brandNameBack", doc.getElementById("brandNameBack").val());
		map.put("promotioncode", doc.getElementById("promotioncodeflag").val());
		map.put("random", random.val());
		p=super.getBasePost(passUrl, map);
		HttpClient h = getHttpClient();
		return Jsoup.parse(excuteRequest(h, p, true));
	}
	
	/**
	 * 下单填写信息页查询(第二种情况)
	 * @param p
	 * @param url
	 * @return
	 * @throws Exception
	 */
	private Document postQuery(HttpPost p,Document doc,int flightOw,int flightRt) throws Exception{
		Map<String,String> map = new HashMap<String, String>();
		Elements div = doc.select(".ins_par1_con");
		Elements names = null;
		for(Element e : div){
			if(e.getElementById("nextform") != null){
				names = e.select("[name]");
				break;
			}
		}
		for(Element nameElement : names){
			String name = nameElement.attr("name");
			if(!name.equals("flightOW") && !name.equals("flightRT")){
				map.put(name, nameElement.val());
			}
		}
		map.put("flightOW", doc.getElementById("trOW-" + flightOw).select("[name=flightOW]").get(0).val());
		if(flightRt != -1){
			map.put("flightRT", doc.getElementById("trRT-" + flightRt).select("[name=flightRT]").get(0).val());
		}
		p=super.getBasePost(passUrl, map);
		HttpClient h = getHttpClient();
		return Jsoup.parse(excuteRequest(h, p, true));
	}
	
	/**
	 * 设置请求页面查询的参数
	 * @param cabinType 舱位类型   （经济舱： ECONOMY，商务舱：BUSINESS）
	 * @param tripType 单程或往返 （单程：OW，往返：RT）
	 */
	private Map<String,String> getAVQueryParams(String cabinType,String tripType){
		Map<String,String> params = new HashMap<String, String>();
		params.put("adultCount", "1");
		params.put("cabinType", cabinType);
		params.put("childCount", "0");
		params.put("dstcity", taskQueue.getToCity());
		params.put("email", "");
		params.put("language", "CN");
		params.put("orgcity", taskQueue.getFromCity());
		params.put("sureDate", "1");
		params.put("takeoffDate", taskQueue.getFlightDate());
		params.put("tripType", tripType);
		if(tripType.equals(roundTrip)){
			params.put("returnDate", taskQueue.getReturnGrabDate());
		}
		return params;
	}
	
	/**
	 * 查询价格
	 * @param doc
	 * @param e
	 * @param fltkey
	 * @return
	 * @throws Exception
	 */
	private String fetchPrice(Document doc,Element e,String fltkey) throws Exception{
		StringBuilder sb = new StringBuilder();
		sb.append("https://book.hongkongairlines.com/hxet/reservation/reservation/FltPriceCal.do");
		sb.append("?fltkey=" + fltkey);
		sb.append("&currency=" + "CNY");
		sb.append("&random=" + doc.getElementById("random").val());
		sb.append("&farefamilyname=" + e.val());
		HttpGet g = new HttpGet(sb.toString());
		setDefaultHeadValue(g);
		HttpClient h = getHttpClient();
		return excuteRequest(h, g, true);
	}
	
	/**
	 * 解析国际单程
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private List<Object> paraseInterOneWay(Object fetchObject) throws Exception{
		return (List<Object>)fetchObject;
	}
	
	/**
	 * 解析国际往返
	 * @param fetchObject 
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private List<Object> paraseInterRound(Object fetchObject) throws Exception{
		return (List<Object>)fetchObject;
	}

	@Override
	public boolean validateFetch(Object fetchObject) throws Exception {
		if(fetchObject == null){
			logger.info("抓取的数据为空");
			return false;
		}
		return true;
	}
	
	private void setDefaultHeadValue(HttpRequestBase request)throws Exception{
		Map<String,String> headValue=new HashMap<String,String>();
		headValue.put("Accept-Language", "zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3");
		headValue.put("Host", "book.hongkongairlines.com");
		headValue.put("Pragma", "no-cache");
	/*	headValue.put("Referer", "http://www.scal.com.cn/B2C/ETicket/InterAirlineList");*/
		/*headValue.put("Referer", "http://www.scal.com.cn/B2C/ETicket/AirlineList");*/
		headValue.put("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0");
		//headValue.put("X-Requested-With", "XMLHttpRequest");
		/*headValue.put("", "");*/
		super.setRequestHead(request, headValue);
	}
	
	  public static void main(String[] args) throws Exception {
		  	long begin = System.currentTimeMillis();
	        TaskModel taskModel = new TaskModel();
	        taskModel.setFromCity("PEK"); 
	        taskModel.setFromCityName("北京");
	        taskModel.setToCity("HKG");
	        taskModel.setToCityName("香港");
	        taskModel.setFlightDate("2014-09-01");
	        taskModel.setIsReturn(0);
	        taskModel.setIsInternational(1);
	        taskModel.setReturnGrabDate("2014-09-05");
	        HongKongAirAdapter adapter = new HongKongAirAdapter(taskModel);
	        Object obj = adapter.fetch(null);
			List<Object> objList = adapter.paraseToVo(obj);
			System.out.println(objList);
			System.out.println((System.currentTimeMillis() - begin) + "ms");
	    }
}
